/*
 * Node.h
 *
 *  Created on: Nov 16, 2009
 *      Author: kevin
 */

#ifndef NODE_H_
#define NODE_H_

#include "master/master.h"
#include "slave/slave.h"
#include "command/commandfactory.h"
using Commands::CommandFactory;

#include <Poco/Types.h>
using Poco::UInt16;
using Poco::UInt32;
using Poco::UInt64;

#include <Poco/File.h>
using Poco::File;

#include <Poco/Runnable.h>
using Poco::Runnable;

#include <vector>
using std::vector;

#include <list>
using std::list;

#include <utility>
using std::pair;

#include <string>
using std::string;

#include <Poco/Thread.h>
using Poco::Thread;

#include <Poco/Semaphore.h>
using Poco::Semaphore;

#include "filemanager/filemanager.h"
#include "multicastconnector/multicastconnector.h"
#include "command/commandqueue.h"
#include "command/dispatcher.h"
using Commands::Dispatcher;

#include <Poco/Timer.h>
using Poco::Timer;
using Poco::TimerCallback;

#include "node/idcollector.h"
#include "broadcaster/broadcaster.h"

typedef idCollector::id_type id_type;
struct NodeData;

/**
 * This class represents a Node. It combines a master and a slave.
 * These can be combined to form a ring, where a node is a master for the next node and a slave
 * from the previous one.
 */
class Node : public Runnable
{
public:
	typedef idCollector::id_type id_type;
	Node();
	virtual ~Node();

	/**
	 * Joins the specified ring with the given ID
	 * @param ring Name of the ring we want to join
	 * @param id Our ID in the ring
	 */
	void joinRing(string ring);

	/**
	 * Joins the node to a subsequent node
	 * @param host Hostname that identifies the next Node
	 * @param port Port on which the next Node is listening for connections
	 */
	void join(string host, UInt16 port);

	/**
	 * Leaves leaves the next Node
	 */
	void leave();

	/**
	 * Puts a file on the next node.
	 * @param filename The filename to be used
	 * @param file The actual file
	 */
	void put(string filename, File file);

	/**
	 * Gets a file from the next node.
	 * @param filename The file to get from the node
	 * @param file The actual file where the file should be stored
	 * @return Whether the could be getted.
	 */
	bool get(string filename, File file);

	/**
	 * Runs the Node Thread
	 */
	void run();

	bool inRing(){
		return fInRing;
	}

	bool hasCommands(){
		return commands->isEmpty();
	}

	void setPrevId(id_type id) { _previd = id; }
	void setNextId(id_type id) { _nextid = id; }

	void runNodeThread(){
		fNodeThread.join();
	}
	//vector< pair< string, FileSize > > listing();
	Thread fNodeThread;

	void printNodeInfo();
private:
	UInt16 fPort;

	bool fInRing;
	string fRingName;

	Master* fMaster;
	Slave* fSlave;

	CommandQueue* commands;
	MulticastConnector fMcast;
	//Dispatcher fDisp;
	CommandFactory fCmdFac;

	Thread fSthread;
	Thread fMthread;
	Thread fMCthread;
	Thread fBroadThread;

	Timer fAloneTimer;

	Semaphore fIdLock;

	//NotificationCenter fNc;

	void onTimer( Timer &timer );

	idCollector fId;

	id_type id();
	bool hasId();

	Broadcaster fBroadcast;
	id_type _nodeid;
	id_type _nextid;
	id_type _previd;

};

struct NodeData {
	id_type node_id;
	id_type next_id;
	id_type prev_id;
	string ringname;
	SocketAddress origin;
	SocketAddress local;
	Master* master;
	UInt16 slaveport;
	Node* node;
};

#endif /* NODE_H_ */
